{
    let a = 10;
    var b = 1;
}
console.log(b);

for(let i=0; i<10; i++){
    console.log(i)
}
// 在这里是不能引用i的，比如 console.log(i) 会报错
var a = []
for(var j = 0; j<10; j++){
    a[j] = function (){
        console.log(j);
    }
}
a[6](); // 10

// let
var a = [];
for (let ii = 0; ii < 10; ii++) {
  a[ii] = function () {
    console.log(ii);
  };
}
a[6](); // 6

/** 
 * 另外，for循环还有一个特别之处，就是设置循环变量的那部分是一个父作用域，
 * 而循环体内部是一个单独的子作用域。
 * */ 
for (let i = 0; i < 3; i++) {
    let i = 'abc';
    console.log(i);
  }
  // abc
  // abc
  // abc
/** 
 * 上面代码正确运行，输出了 3 次abc。
 * 这表明函数内部的变量i与循环变量i不在同一个作用域，有各自单独的作用域。
 * */   

//  不存在变量提升状态
console.log(bar); //undefined
var bar = 2;

// console.log(ba); 直接报错
let ba = 2;

// 暂时性死区
var tmp = 123;

if (true) {
//   tmp = 'abc'; // ReferenceError
// 阻止了变量提升
  let tmp;
}

// typeof x; // ReferenceError
let x;


// 作为比较，如果一个变量根本没有被声明，使用typeof反而不会报错。
typeof undeclared_variable // "undefined"

// 有些“死区”比较隐蔽，不太容易发现。
function bar(x = y, y = 2) {
    return [x, y];
  }
  
  bar(); // 报错


  function bar(x = 2, y = x) {
    return [x, y];
  }
  bar(); // [2, 2]

//   另外，下面的代码也会报错，与var的行为不同。
// 不报错
var x = x;

// 报错
// let x = x;
// ReferenceError: x is not defined

// 主要是为了减少运行时错误，防止在变量声明前就使用这个变量，从而导致意料之外的行为

// let 不允许在相同作用域内，重复定义同一个变量
// 报错
function func() {
    let a = 10;
    var a = 1;
  }
  
  // 报错
  function func() {
    let a = 10;
    let a = 1;
  }

//   不能在函数内部重新声明参数。
function func(arg) {
    let arg;
  }
  func() // 报错
  
  function func(arg) {
    {
      let arg;
    }
  }
  func() // 不报错

//   块级作用域
// 第一种场景，内层变量可能会覆盖外层变量。
var tmp = new Date();

function f() {
  console.log(tmp);
  if (false) {
    var tmp = 'hello world';
  }
}

f(); // undefined 变量提升

// 第二种场景，用来计数的循环变量泄露为全局变量。
var s = 'hello';

for (var i = 0; i < s.length; i++) {
  console.log(s[i]);
}

console.log(i); // 5

// ES6 的块级作用域
function f1() {
    let n = 5;
    if (true) {
      let n = 10;
    }
    console.log(n); // 5
  }

//   块级作用域与函数声明
// ES5 规定，函数只能在顶层作用域和函数作用域之中声明，不能在块级作用域声明。
// 情况一
if (true) {
    function f() {}
  }
  
  // 情况二
  try {
    function f() {}
  } catch(e) {
    // ...
  }
//   上面两种函数声明，根据 ES5 的规定都是非法的。

/** 
 * 另外，还有一个需要注意的地方。
 * ES6 的块级作用域允许声明函数的规则，
 * 只在使用大括号的情况下成立，
 * 如果没有使用大括号，就会报错。
 * */ 
// 不报错
'use strict';
if (true) {
  function f() {}
}

// 报错
'use strict';
if (true)
  function f() {}


/**
 * 考虑到环境导致的行为差异太大，应该避免在块级作用域内声明函数。
 * 如果确实需要，也应该写成函数表达式，而不是函数声明语句。
 *  */ 
// 函数声明语句
{
    let a = 'secret';
    function f() {
      return a;
    }
  }
  
  // 函数表达式
  {
    let a = 'secret';
    let f = function () {
      return a;
    };
  }

/** 
 * 允许在块级作用域内声明函数。
 * 函数声明类似于var，即会提升到全局作用域或函数作用域的头部。
 * 同时，函数声明还会提升到所在的块级作用域的头部。
*/
// 浏览器的 ES6 环境
function f() { console.log('I am outside!'); }

(function () {
  if (false) {
    // 重复声明一次函数f
    function f() { console.log('I am inside!'); }
  }

  f();
}());
// Uncaught TypeError: f is not a function
// 实际上上面代码运行的是这样的代码
// 浏览器的 ES6 环境
function f() { console.log('I am outside!'); }
(function () {
  var f = undefined;
  if (false) {
    function f() { console.log('I am inside!'); }
  }

  f();
}());
// Uncaught TypeError: f is not a function

